// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose:  Implementation of some DOM wrappers that never got finished.
//   $Id$


#include "stdafx.h"
#include "XForms.h"
#include "DOM3Core.h"


/////////////////////////////////////////////////////////////////////////////
//
CComPtr<IClassFactory> CDOM3Document::sm_pDOMDocumentFactory;
HANDLE CDOM3Document::sm_hDOMDocumentFactoryMutex = 0;

void CDOM3Document::DestroyFactory()
{
	WaitForSingleObject(sm_hDOMDocumentFactoryMutex, INFINITE);

	try
	{
		if(sm_pDOMDocumentFactory)
		{
			sm_pDOMDocumentFactory->LockServer(FALSE);
			sm_pDOMDocumentFactory.Release();
		}
	}
	catch(...)
	{
	}

	ReleaseMutex(sm_hDOMDocumentFactoryMutex);
}

HRESULT CDOM3Document::CreateDOMDocument()
{
	HRESULT hr = S_FALSE;
	
	WaitForSingleObject(sm_hDOMDocumentFactoryMutex, INFINITE);

	try
	{
		if(!sm_pDOMDocumentFactory) 
		{
			hr = ::CoGetClassObject(CLSID_DOMDocument60, CLSCTX_ALL, NULL, __uuidof(IClassFactory),  reinterpret_cast<void**>(&sm_pDOMDocumentFactory));
			if(sm_pDOMDocumentFactory)
			{
				sm_pDOMDocumentFactory->LockServer(TRUE);
			}
		}

		if(sm_pDOMDocumentFactory)
			hr = sm_pDOMDocumentFactory->CreateInstance(NULL, IID_IXMLDOMDocument, reinterpret_cast<void**>(&m_pXMLParser));
		else
		{
			ATLTRACE("!formsplayer.dll: Failed to create CLSID_DOMDocument60 classfactory, using CoCreateInstance Instead \n");
			hr = CoCreateInstance(CLSID_DOMDocument60, NULL, CLSCTX_ALL, IID_IXMLDOMDocument, reinterpret_cast<void**>(&m_pXMLParser));
		}
	}
	catch(...)
	{
	}

	ReleaseMutex(sm_hDOMDocumentFactoryMutex);

	return hr;
}

CDOM3Document::CDOM3Document()
{
	if(!sm_hDOMDocumentFactoryMutex)
		sm_hDOMDocumentFactoryMutex = CreateMutex(0, FALSE, _T("Local\\DOM3DocumentFactoryMutex"));

	HRESULT hr = CreateDOMDocument();;
	//IXMLDOMDocument2* pXMLDoc2 = NULL;
//	hr =pXMLDoc2.CoCreateInstance(CLSID_FreeThreadedDOMDocument40, NULL,CLSCTX_SERVER);
	
	CComQIPtr<IXMLDOMDocument2> pXMLDoc2(m_pXMLParser);
//	hr = CoCreateInstance(CLSID_FreeThreadedDOMDocument40, NULL, CLSCTX_SERVER, IID_IXMLDOMDocument2,
	//hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument,
//		(void**) &pXMLDoc2);

	if (S_OK == hr)
	{

		/*
		 * The default for the XML 4.0 is to use the old parser, but this will probably change
		 * in the future
		 */
		
		CComBSTR bstr = _T("NewParser");
		CComVariant v = true;

		pXMLDoc2->setProperty(bstr, v);
		
		/*
		 * Any queries on the document need to use XPath - not the old XSL
		 */
		bstr.Empty();
		bstr = _T("SelectionLanguage");
		v = _T("XPath");
		pXMLDoc2->setProperty(bstr, v);
		pXMLDoc2->setProperty(CComBSTR(L"ProhibitDTD"), CComVariant(false));

		m_pXMLParser->put_preserveWhiteSpace(VARIANT_TRUE);
		m_pXMLParser->put_validateOnParse(VARIANT_FALSE);
		m_pXMLParser->put_async(VARIANT_FALSE);
	}

}

CDOM3Document::CDOM3Document(IXMLDOMDocument *pDoc)
: m_pXMLParser(pDoc)
{
	if(!sm_hDOMDocumentFactoryMutex)
		sm_hDOMDocumentFactoryMutex = CreateMutex(0, FALSE, _T("Local\\DOM3DocumentFactoryMutex"));
}

CDOM3Document::~CDOM3Document()
{
}

CDOMString CDOM3Document::get_documentURI()
{
	/*
		http://www.w3.org/TR/2002/WD-DOM-Level-3-Core-20020409/core.html#Document3-documentURI

	documentURI of type DOMString, introduced in DOM Level 3
		The location of the document or null if undefined.
		Beware that when the Document supports the feature 
		"HTML" [DOM Level 2 HTML], the href attribute of the
		HTML BASE element takes precedence over this attribute.

	*/
	// HTML BASE caveat not implemented.

	CComBSTR bsURL;
	HRESULT hr;
	hr = m_pXMLParser->get_url(&bsURL);
	if(S_OK != hr) //S_FALSE = no URL, E_ is error retrieving
		return NULL;
	else
		return bsURL.Copy();
}

CDOMString CDOM3Node::get_baseURI()
{
	/*
		http://www.w3.org/TR/2002/WD-DOM-Level-3-Core-20020409/core.html#Node3-baseURI

		baseURI of type DOMString, readonly, introduced in DOM Level 3
			The absolute base URI of this node or null if undefined.
			This value is computed according to [XML Base].
			However, when the Document supports the feature "HTML"
			[DOM Level 2 HTML], the base URI is computed using first
			the value of the href attribute of the HTML BASE element
			if any, and the value of the documentURI attribute from
			the Document interface otherwise.

			When the node is an Element, a Document or a
			ProcessingInstruction, this attribute represents the
			properties [base URI] defined in [XML Information set].

			When the node is a Notation, an Entity, or an EntityReference,
			this attribute represents the properties [declaration base URI]
			in the [XML Information set]. 
	*/
	return NULL;

}

CDOM3Document * CDOM3Node::get_ownerDocument()
{
	/*
		http://www.w3.org/TR/2002/WD-DOM-Level-3-Core-20020409/core.html#node-ownerDoc
		
		  ownerDocument of type Document, readonly, modified in DOM Level 2
			The Document object associated with this node. 
			This is also the Document object used to create new nodes. 

			When this node is a Document or a DocumentType which is not used 
			with any Document yet, this is null.
	*/
	return NULL;
}

CXSLTemplate::CXSLTemplate()
{
	HRESULT hr;
	IXSLTemplate* pXSL = NULL;

	hr = CoCreateInstance(CLSID_XSLTemplate60, NULL, CLSCTX_INPROC_SERVER, IID_IXSLTemplate,
		(void**) &pXSL);

	if (S_OK == hr)
		m_pXSLTemplate = pXSL;
	else
		m_pXSLTemplate = NULL;
}

CXSLTemplate::~CXSLTemplate()
{
	::safeRelease(m_pXSLTemplate);
}

CXMLRequest::CXMLRequest()
	:m_pXMLRequest(0)
{
	HRESULT hr;
	IXMLHTTPRequest* pXMLRequest = NULL;

	hr = CoCreateInstance(CLSID_XMLHTTPRequest, NULL, CLSCTX_INPROC_SERVER, IID_IXMLHTTPRequest,
		(void**) &pXMLRequest);

	if (S_OK == hr)
	{
		m_pXMLRequest = pXMLRequest;
	}
}

CXMLRequest::~CXMLRequest()
{
	::safeRelease(m_pXMLRequest);
}



HRESULT CXMLRequest::postNode(CNode *pNode, BSTR sURI)
{
	HRESULT hr = E_PENDING;
	if(m_pXMLRequest)
	{
		hr = S_OK;
		CComVariant vFalse = false;
		CComVariant vEmpty = _T("");
		CComBSTR sPost("post");
		hr = m_pXMLRequest->open(sPost, sURI, vFalse, vEmpty, vEmpty);
		if (S_OK == hr)
		{
			CComVariant v(pNode);
			hr = m_pXMLRequest->send(v);
		}
	}
	return hr;
}

HRESULT CXMLRequest::sendTextAsync(VARIANT * pv, BSTR sURI, CComBSTRMap &HeadersList, BSTR sType,const BSTR sMethod, const BSTR sUsername, const BSTR sPassword, IDispatch* pOnChange)
{
	HRESULT hr = E_PENDING;
	if(m_pXMLRequest)
	{
		hr = S_OK;
		CComVariant vTrue = true;
		CComVariant vUsername = sUsername;
		CComVariant vPassword = sPassword;

		bool bContentTypeSet = false;
		// Content-Type header name
		CComBSTR sContentType("CONTENT-TYPE");
		// Open the connection
		hr = m_pXMLRequest->open(sMethod, sURI, vTrue, vUsername, vPassword);
		m_pXMLRequest->put_onreadystatechange(pOnChange);
		// Add any headers provided by the HeadersList param
		CComBSTRMap::iterator p = HeadersList.begin();
		while(p != HeadersList.end()) {
			// We've added the ContentType Header we need to 
			// indicate so we don't append the original value to 
			// the end. It appears that setRequestHeader() append the 
			// values to headers that have already been added.
			if(wcsicmp(p->first, sContentType) == 0)
				bContentTypeSet = true;
			// Add the addition header provided by HeadersList
			m_pXMLRequest->setRequestHeader(p->first, p->second);
			// Move to the next 
			p++;
		}
		// If there is a ContentType provided and its has been
		// overwritten by the HeadersList param, then set it.
		if(sType && !bContentTypeSet) 
			m_pXMLRequest->setRequestHeader(sContentType,sType);
		// Actually send the request
		if (S_OK == hr) {
			hr = m_pXMLRequest->send(*pv);
		}
	}
	return hr;
}

HRESULT CXMLRequest::sendText(BSTR sText, BSTR sURI, CComBSTRMap &HeadersList, BSTR sType,const BSTR sMethod, const BSTR sUsername, const BSTR sPassword)
{
	HRESULT hr = E_PENDING;
	if(m_pXMLRequest)
	{
		hr = S_OK;
		CComVariant vFalse = false;
		CComVariant vUsername = sUsername;
		CComVariant vPassword = sPassword;

		bool bContentTypeSet = false;
		// Content-Type header name
		CComBSTR sContentType("CONTENT-TYPE");
		// Open the connection
		hr = m_pXMLRequest->open(sMethod, sURI, vFalse, vUsername, vPassword);
		// Add any headers provided by the HeadersList param
		CComBSTRMap::iterator p = HeadersList.begin();
		while(p != HeadersList.end()) {
			// We've added the ContentType Header we need to 
			// indicate so we don't append the original value to 
			// the end. It appears that setRequestHeader() append the 
			// values to headers that have already been added.
			if(wcsicmp(p->first, sContentType) == 0)
				bContentTypeSet = true;
			// Add the addition header provided by HeadersList
			m_pXMLRequest->setRequestHeader(p->first, p->second);
			// Move to the next 
			p++;
		}
		// If there is a ContentType provided and its has been
		// overwritten by the HeadersList param, then set it.
		if(sType && !bContentTypeSet) 
			m_pXMLRequest->setRequestHeader(sContentType,sType);
		// Actually send the request
		if (S_OK == hr) {
			if(sText && *sText)
			{
				CComVariant v(sText);
				hr = m_pXMLRequest->send(v);
			}
			else
			{
				CComVariant v;
				hr = m_pXMLRequest->send(v);
			}
		}
	}
	return hr;
}

HRESULT CXMLRequest::sendText(VARIANT * pvtData, BSTR sURI, CComBSTRMap &HeadersList, BSTR sType, const BSTR sMethod, const BSTR sUsername, const BSTR sPassword)
{
	HRESULT hr = E_PENDING;
	if(m_pXMLRequest)
	{
		hr = S_OK;
		CComVariant vFalse = false;
		CComVariant vUsername = sUsername;
		CComVariant vPassword = sPassword;
		bool bContentTypeSet = false;
		// Content-Type header name
		CComBSTR sContentType("CONTENT-TYPE");
		// Open the connection
		hr = m_pXMLRequest->open(sMethod, sURI, vFalse, vUsername, vPassword);
		// Add any headers provided by the HeadersList param
		CComBSTRMap::iterator p = HeadersList.begin();
		while(p != HeadersList.end()) {
			// We've added the ContentType Header we need to 
			// indicate so we don't append the original value to 
			// the end. It appears that setRequestHeader() append the 
			// values to headers that have already been added.
			if(wcsicmp(p->first, sContentType) == 0)
				bContentTypeSet = true;
			// Add the addition header provided by HeadersList
			m_pXMLRequest->setRequestHeader(p->first, p->second);
			// Move to the next 
			p++;
		}
		// If there is a ContentType provided and its has been
		// overwritten by the HeadersList param, then set it.
		if(sType && !bContentTypeSet) 
			m_pXMLRequest->setRequestHeader(sContentType,sType);
		// Actually send the request
		if (S_OK == hr) 
			hr = m_pXMLRequest->send(*pvtData);
	}
	return hr;
}

HRESULT CXMLRequest::AddHeader(BSTR sName, BSTR sValue)
{
	if(m_pXMLRequest)
		return m_pXMLRequest->setRequestHeader(sName,sValue);
	else
		return E_FAIL;
}
